home *** CD-ROM | disk | FTP | other *** search
/ Multimedia Selection / Multimedia Selection Volume One - CD-ROM / MULTIMEDIA SELECTION____________.ISO / utils / bcopy / bcopy.asm next >
Encoding:
Assembly Source File  |  1989-03-17  |  49.4 KB  |  1,241 lines

  1.  
  2. ;=============================================================================
  3. ; BCOPY 1.01 Background copy utility
  4. ;
  5. ; Revision History
  6. ;
  7. ;    1.0   Initial Release
  8. ;    1.1   Fixed bug in DTA restore code.      Jan. 8 1989    DMB
  9. ;    1.2   Fixed bug in queue load code.       Feb. 7 1989    DMB
  10. ;
  11. ;=============================================================================
  12. CODE            SEGMENT PARA PUBLIC 'CODE'
  13.         ASSUME  CS:CODE
  14.  
  15.         ORG     80H
  16. COMMAND_TAIL    DB      ?
  17.         ORG     100H
  18. BEGIN:          JMP     INITIALIZE
  19.  
  20. PROGRAM         DB      "BCOPY 1.2  (c) 1989 Ziff Communications Co.",13,10
  21.         DB      "PC Magazine ",254," Douglas Boling",13,10
  22.                DB    "Usage: BCOPY [source [target]][/X][/U]$",26
  23.  
  24. ;-----------------------------------------------------------------------------
  25. ; Memory locations required for system overhead.
  26. ;-----------------------------------------------------------------------------
  27. INDOS           DD      0                       ;pointer to INDOS flag
  28. CEF_PTR         DD      0                       ;pointer to Critical err flag
  29. DOS_VERSION     DW      0                       ;DOS version number
  30.  
  31. COUNTER         DB      16                      ;request flag/timer
  32. DISKFLAG        DB      0                       ;disk access flag
  33. ACTIVE          DB      0                       ;background status flag
  34. REMOVE_FLAG     DB      0                       ;1 = uninstall when q empty
  35. RET_ADDR        DW      0                       ;saved return addr for calls
  36.  
  37. SOURCE_HNDL     DW      0                       ;Source file handle
  38. DEST_HNDL       DW      0                       ;Destination handle
  39.  
  40. EMS_FLAG        DB      0                       ;Use expanded memory
  41. EMS_HANDLE      DW      0                       ;EMS handle used
  42.  
  43. DATA_SEGMENT    DW      0                       ;segment of data buffer
  44. QUEUE_HEAD_PTR  DW      0                       ;pointer to first file in q
  45. FILE_COUNT      DB      0                       ;Number of files in queue
  46. DATA_BUFF_START DW      OFFSET DATA_BUFFER      ;pointer to start of buffer
  47. DATA_BUFF_END   DW      OFFSET END_OF_DATA      ;pointer to end of data buffer
  48.  
  49. INT8H           DD      0                       ;int 8h vector  (Timer)
  50. INT13H          DD      0                       ;int 13h vector (Disk)
  51. INT28H          DD      0                       ;int 28h vector (Idle)
  52.  
  53. SAVED_DTA       DD      0                       ;saved pointer to curr DTA
  54. SAVED_PSP       DW      0                       ;saved segment of curr PSP
  55. SS_REGISTER     DW      0                       ;SS register
  56. SP_REGISTER     DW      0                       ;SP register
  57.  
  58. ERRINFOARRAY    DW      6 DUP (0)               ;Saved extended error info
  59. ERRINFODS       DW      0
  60. ERRINFOES       DW      0
  61.         DW      3 DUP (0)               ;Reserved error table bytes
  62.  
  63. VECTOR1BH       DD      0                       ;int 1Bh vector (Break)
  64. VECTOR23H       DD      0                       ;int 23h vector (Ctrl-C)
  65. VECTOR24H       DD      0                       ;int 24h vector (Crit err)
  66.  
  67. ;=============================================================================
  68. ; TIMERINT receives control when an interrupt 8 is generated.
  69. ;=============================================================================
  70. TIMERINT        PROC    FAR
  71.         ASSUME  CS:CODE,DS:NOTHING,ES:NOTHING
  72.         PUSHF                           ;call BIOS routine
  73.         CALL    INT8H
  74.         CLI                             ;make sure interrupts are off
  75.         CMP     CS:COUNTER,0            ;exit if timer not expired
  76.         JG      DECTIME
  77.         CMP     CS:ACTIVE,0             ;See if already active.
  78.         JNE     TIMER_EXIT
  79.         CMP     CS:DISKFLAG,0           ;check disk access status
  80.         JNE     TIMER_EXIT              ;exit if disk active.
  81.         PUSH    ES
  82.         PUSH    DI
  83.         LES     DI,INDOS                ;retrieve INDOS address
  84.         CMP     BYTE PTR ES:[DI],0      ;is the INDOS flag clear?
  85.         POP     DI
  86.         POP     ES
  87.         JNE     TIMER_EXIT
  88.         CMP     CS:FILE_COUNT,0         ;See if any files in queue
  89.         JNE     TIMER_CONTINUE
  90.         CMP     CS:REMOVE_FLAG,0        ;See if routine should be
  91.         JE      TIMER_EXIT              ;  removed from memory
  92.         CALL    REMOVE
  93.         JNC     TIMER_EXIT              ;If no error, exit. If error,
  94.         JMP     SHORT TIMER_SKIP_MAIN   ;  wait then try again.
  95. TIMER_CONTINUE:
  96.         CALL    MAIN                    ;yes, invoke background routine
  97. TIMER_SKIP_MAIN:
  98.         MOV     CS:COUNTER,17           ;Set sleep counter = 1 sec
  99. DECTIME:
  100.         DEC     CS:COUNTER              ;decrement wait counter
  101. TIMER_EXIT:
  102.         IRET
  103. TIMERINT        ENDP
  104.  
  105. ;=============================================================================
  106. ; IDLE receives control when an interrupt 28h is generated.
  107. ;=============================================================================
  108. IDLE            PROC    FAR
  109.         ASSUME  CS:CODE,DS:NOTHING,ES:NOTHING
  110.         PUSHF                           ;call DOS routine
  111.         CALL    INT28H
  112.         CLI                             ;make sure interrupts are off
  113.         CMP     CS:FILE_COUNT,0         ;See if files in queue
  114.         JE      IDLE_EXIT               ;If not, exit.
  115.         CMP     CS:COUNTER,8            ;wait at least 1/2 sec between
  116.         JA      IDLE_EXIT               ;  calls.
  117.         CMP     CS:ACTIVE,0             ;See if already active.
  118.         JNE     IDLE_EXIT
  119.         CMP     CS:DISKFLAG,0           ;check disk access status
  120.         JNE     IDLE_EXIT               ;exit if flag is raised
  121.         PUSH    ES
  122.         PUSH    DI
  123.         LES     DI,CS:CEF_PTR           ;retrieve crit err flag adr
  124.         CMP     BYTE PTR ES:[DI],0      ;is DOS in crit error state?
  125.         POP     DI
  126.         POP     ES
  127.         JNE     IDLE_EXIT               ;yes, don't do anything
  128.         CALL    MAIN                    ;Call background routine.
  129.         MOV     CS:COUNTER,16           ;Set sleep counter
  130. IDLE_EXIT:
  131.         IRET
  132. IDLE            ENDP
  133.  
  134. ;=============================================================================
  135. ; DISKINT receives control when an interrupt 13h is generated.
  136. ;=============================================================================
  137. DISKINT         PROC    FAR
  138.         ASSUME  CS:CODE,DS:NOTHING,ES:NOTHING
  139.         PUSHF                           ;save flags register
  140.         INC     CS:DISKFLAG             ;set disk access flag
  141.         POPF                            ;restore flags
  142.         PUSHF                           ;call BIOS routine
  143.         CALL    INT13H
  144.         PUSHF                           ;save flags again
  145.         DEC     CS:DISKFLAG             ;reset disk access flag
  146.         POPF                            ;restore flags
  147.         RET     2                       ;exit with flags intact
  148. DISKINT         ENDP
  149.  
  150. ;=============================================================================
  151. ; CRITICALERR receives control when an interrupt 24h is generated.
  152. ;=============================================================================
  153. CRITICALERR     PROC    FAR
  154.         ASSUME  CS:CODE,DS:NOTHING,ES:NOTHING
  155.         XOR     AL,AL                   ;Default to ignore
  156.         CMP     CS:DOS_VERSION,30AH     ;See if before DOS 3.1
  157.         JL      CRITICAL1
  158.         ADD     AL,3
  159. CRITICAL1:
  160.         IRET
  161. CRITICALERR     ENDP
  162.  
  163. ;=============================================================================
  164. ; MAIN
  165. ;=============================================================================
  166. MAIN            PROC    NEAR
  167.         ASSUME  CS:CODE,DS:NOTHING,ES:NOTHING
  168.         INC     CS:[ACTIVE]             ;set program status flag
  169.         CLI                             ;make sure interrupts are off
  170.         MOV     CS:SS_REGISTER,SS       ;save SS and SP registers
  171.         MOV     CS:SP_REGISTER,SP
  172.         PUSH    CS                      ;switch to internal stack
  173.         POP     SS
  174.         MOV     SP,OFFSET STACK_SPACE
  175.         STI                             ;enable interrupts
  176.         CALL    SAVE_REGS               ;save all registers
  177.         ASSUME  DS:CODE
  178. ;-----------------------------------------------------------------------------
  179. ;Point the interrupt 1Bh, 23h, and 24h vectors to internal handlers.
  180. ;-----------------------------------------------------------------------------
  181.         MOV     AX,351BH                ;get and save 1Bh vector
  182.         INT     21H
  183.         MOV     WORD PTR VECTOR1BH,BX
  184.         MOV     WORD PTR VECTOR1BH[2],ES
  185.         MOV     AX,251BH                ;point interrupt to IRET
  186.         MOV     DX,OFFSET IDLE_EXIT
  187.         INT     21H
  188.  
  189.         MOV     AX,3523H                ;get and save 23h vector
  190.         INT     21H
  191.         MOV     WORD PTR VECTOR23H,BX
  192.         MOV     WORD PTR VECTOR23H[2],ES
  193.         MOV     AX,2523H                ;point interrupt to IRET
  194.         MOV     DX,OFFSET IDLE_EXIT
  195.         INT     21H
  196.  
  197.         MOV     AX,3524H                ;get and save 24h vector
  198.         INT     21H
  199.         MOV     WORD PTR VECTOR24H,BX
  200.         MOV     WORD PTR VECTOR24H[2],ES
  201.         MOV     AX,2524H                ;point interrupt to internal
  202.         PUSH    CS
  203.         POP     DS
  204.         MOV     DX,OFFSET CRITICALERR   ;  critical error handler
  205.         INT     21H
  206. ;-----------------------------------------------------------------------------
  207. ;Save and switch to internal PSP
  208. ;-----------------------------------------------------------------------------
  209.         MOV     AH,51H                  ;Get current PSP
  210.         CALL    DOSPSPCALL              ;Beware DOS 2.0 - 3.0
  211.         MOV     SAVED_PSP,BX            ;save it
  212.         PUSH    CS
  213.         POP     BX
  214.         MOV     AH,50H                  ;Set internal PSP
  215.         CALL    DOSPSPCALL
  216. ;-----------------------------------------------------------------------------
  217. ;Save and switch to internal DTA
  218. ;-----------------------------------------------------------------------------
  219.         MOV     AH,2FH
  220.         INT     21H                     ;Get current DTA
  221.         MOV     WORD PTR SAVED_DTA,BX   ;save it
  222.         MOV     WORD PTR SAVED_DTA[2],ES
  223.         MOV     DX,OFFSET COMMAND_TAIL  ;use PSP for DTA
  224.         MOV     AH,1AH                  ;Set DTA
  225.         INT     21H
  226. ;-----------------------------------------------------------------------------
  227. ;If DOS >= 3.x, save extended error information.
  228. ;-----------------------------------------------------------------------------
  229.         CMP     WORD PTR DOS_VERSION,030AH
  230.         JB      SKIP_ERR_SAVE
  231.         PUSH    DS                      ;save DS
  232.         XOR     BX,BX                   ;clear BX for call
  233.         MOV     AH,59H                  ;Extended error info
  234.         INT     21H                     ;Call DOS
  235.         MOV     CS:ERRINFODS,DS         ;save returned ES
  236.         POP     DS                      ;Restore DS
  237.         PUSH    BX
  238.         MOV     BX,OFFSET ERRINFOARRAY  ;Save data in registers
  239.         MOV     [BX],AX                 ;  in this specific order.
  240.         POP     2[BX]
  241.         MOV     4[BX],CX
  242.         MOV     6[BX],DX
  243.         MOV     8[BX],SI
  244.         MOV     0AH[BX],DI
  245.         MOV     0EH[BX],ES
  246. SKIP_ERR_SAVE:
  247. ;-----------------------------------------------------------------------------
  248. ;If using EMS memory, save EMS mapping context and map our page.
  249. ;-----------------------------------------------------------------------------
  250.         CMP     EMS_FLAG,0
  251.         JE      EMS_SAVE_SKIP
  252.         MOV     AH,47H                  ;Save mapping context
  253.         MOV     DX,EMS_HANDLE
  254.         INT     67H
  255.         OR      AH,AH
  256.         JNE     JMP_CLEAN_UP
  257.         MOV     AX,4400H                ;Map page
  258.         XOR     BX,BX
  259.         MOV     DX,EMS_HANDLE
  260.         INT     67H
  261.         OR      AH,AH
  262.         JNE     JMP_CLEAN_UP
  263. EMS_SAVE_SKIP:
  264.         MOV     ES,DATA_SEGMENT
  265.         MOV     DS,DATA_SEGMENT
  266.         ASSUME  DS:NOTHING
  267. ;-----------------------------------------------------------------------------
  268. ;Check flags in queue to determine if starting a copy or in the middle of one.
  269. ;-----------------------------------------------------------------------------
  270.         MOV     SI,CS:QUEUE_HEAD_PTR    ;Get pointer to filename
  271.         MOV     AX,[SI+2]               ;Get source file flags
  272.         OR      AH,AH                   ;See if source file open
  273.         JNE     OPEN_DEST               ;Yes, check destination file
  274. ;-----------------------------------------------------------------------------
  275. ;Search for source file.
  276. ;-----------------------------------------------------------------------------
  277. FIND_SOURCE:
  278.         ADD     SI,6                    ;move pointer to filename
  279.         MOV     DX,SI                   ;Copy queue pointer
  280.         XOR     CX,CX                   ;Search for normal files.
  281.         MOV     AH,4EH                  ;assume find first
  282.         OR      AL,AL                   ;See if first search.
  283.         JE      FIND1ST                 ;Yes, find first.
  284.         INC     AH                      ;No, change cmd to find next.
  285. FIND1ST:
  286.         INT     21H                     ;Call DOS
  287.         JNC     OPEN_FILE
  288.         JMP     PURGE_FROM_QUEUE        ;If file not found, purge
  289. ;-----------------------------------------------------------------------------
  290. ;Attempt to open source file.
  291. ;-----------------------------------------------------------------------------
  292. OPEN_FILE:
  293.         XOR     DL,DL                   ;indicate source file
  294.         CALL    GETPATHFILE             ;combine path and filename
  295.         MOV     DX,CS:DATA_BUFF_START   ;point to filename
  296.         MOV     AX,3D00H                ;open, read only access
  297.         INT     21H
  298.         JNC     GOOD_OPEN
  299. ;error in file open.
  300.         CMP     AX,4                    ;See if too many open files.
  301.         JNE     OPEN1
  302. JMP_CLEAN_UP:
  303.         JMP     CLEAN_UP                ;If so, try again later
  304. OPEN1:
  305.         JMP     PURGE_FROM_QUEUE        ;Else, bad filename.
  306. GOOD_OPEN:
  307.         MOV     SOURCE_HNDL,AX          ;Save handle for source file
  308.         MOV     SI,CS:QUEUE_HEAD_PTR    ;Get queue ptr, indicate that
  309.         MOV     WORD PTR [SI+2],0101H   ;  1st file opened.
  310. ;-----------------------------------------------------------------------------
  311. ;Create destination file.
  312. ;  Note: open dest assumes SI points to the first entry in the queue.
  313. ;-----------------------------------------------------------------------------
  314. OPEN_DEST:
  315.         MOV     AX,[SI+4]               ;Get destination file flags
  316.         OR      AL,AL                   ;See if destination file open
  317.         JNE     READ_FILE               ;Dest file open, copy data.
  318.         CALL    GET_DEST_NAME           ;Gen filename from queue
  319.         MOV     AH,3CH                  ;create destination file
  320.         XOR     CX,CX                   ;normal attributes
  321.         INT     21H
  322.         JNC     GOOD_CREATE_DEST
  323. OPEN_DEST_ERR:
  324.         CMP     AX,4                    ;See if too many files
  325.         JE      JMP_CLEAN_UP            ;If so, wait till later
  326.         JMP     SHORT PURGE_FROM_QUEUE  ;If other error, purge file
  327. GOOD_CREATE_DEST:
  328.         MOV     CS:DEST_HNDL,AX         ;Save destination handle
  329.         MOV     SI,CS:QUEUE_HEAD_PTR    ;Get pointer to filename
  330.         MOV     BYTE PTR [SI+4],1       ;Set destination file open
  331. ;-----------------------------------------------------------------------------
  332. ;Files are open, read from destination file into data buffer.
  333. ;-----------------------------------------------------------------------------
  334. READ_FILE:
  335.         MOV     CX,CS:DATA_BUFF_END     ;Find end of buffer
  336.         SUB     CX,CS:DATA_BUFF_START   ;Get size of buffer
  337.         SUB     CX,4
  338.         MOV     BX,CS:SOURCE_HNDL       ;Get handle
  339.         MOV     DX,CS:DATA_BUFF_START   ;Point to data buffer
  340.         MOV     AH,3FH                  ;Read file
  341.         INT     21H                     ;Call DOS
  342.         JNC     WRITE_FILE              ;If no error, continue
  343.         MOV     DI,1                    ;If error, close files,
  344.         JMP     SHORT CLOSE_FILES       ;  erase dest, and purge.
  345. ;-----------------------------------------------------------------------------
  346. ;Write data to file.
  347. ;-----------------------------------------------------------------------------
  348. WRITE_FILE:
  349.         MOV     SI,CX                   ;Save num of bytes requested
  350.         MOV     CX,AX                   ;Write number of bytes read.
  351.         MOV     AH,40H                  ;Write file
  352.         MOV     BX,CS:DEST_HNDL         ;To destination
  353.         INT     21H                     ;Call DOS
  354.         JC      WRITE_BAD
  355.         XOR     DI,DI                   ;Use DI as disk full flag
  356.         CMP     AX,CX                   ;Make sure all bytes written
  357.         JE      CHECK_FOR_EOF           ;If not, disk full.
  358. WRITE_BAD:
  359.         INC     DI                      ;set flag to delete file
  360.         JMP     SHORT CLOSE_FILES
  361. ;-----------------------------------------------------------------------------
  362. ;If copy complete, close files
  363. ;-----------------------------------------------------------------------------
  364. CHECK_FOR_EOF:
  365.         CMP     SI,AX                   ;see if at end of file.
  366.         JE      CLEAN_UP                ;No, skip close
  367. CLOSE_FILES:
  368.         MOV     SI,CS:QUEUE_HEAD_PTR    ;Get queue ptr, indicate that
  369.         MOV     WORD PTR [SI+3],0       ;Indicate files closed.
  370.         MOV     BX,CS:SOURCE_HNDL
  371.         MOV     AH,3EH                  ;Close source file
  372.         INT     21H
  373.         MOV     BX,CS:DEST_HNDL
  374.         MOV     AH,3EH                  ;Close destination file
  375.         INT     21H
  376.         OR      DI,DI                   ;See if error during write.
  377.         JE      SHORT CLEAN_UP          ;  If so, delete and purge.
  378.         CALL    GET_DEST_NAME           ;Gen dest file name again
  379.         MOV     AH,41H                  ;Delete partial dest file
  380.         INT     21H                     ;  (DX still points to name.)
  381. ;-----------------------------------------------------------------------------
  382. ;Purge file name from queue.
  383. ;-----------------------------------------------------------------------------
  384. PURGE_FROM_QUEUE:
  385.         MOV     DI,CS:QUEUE_HEAD_PTR    ;Get queue pointer
  386.         MOV     SI,DS:[DI]              ;Get pointer to next file
  387. PURGE_FILE_LOOP:
  388.         CMP     WORD PTR DS:[SI],0FFFFH ;See if good file
  389.         JE      PURGE_DONE
  390.         MOV     CX,DS:[SI]              ;Compute length of entry
  391.         SUB     CX,SI
  392.         MOV     BX,DI                   ;save pointer to entry
  393.         REP     MOVSB                   ;Copy queue entry
  394.         MOV     DS:[BX],DI              ;Update pointer
  395.         JMP     PURGE_FILE_LOOP
  396. PURGE_DONE:
  397.         MOV     WORD PTR DS:[DI],0FFFFH ;copy end pointer
  398.         INC     DI
  399.         INC     DI
  400.         MOV     CS:DATA_BUFF_START,DI   ;Update start of data buffer.
  401.         DEC     CS:FILE_COUNT
  402. ;-----------------------------------------------------------------------------
  403. ;Clean up DOS for return to forground task. Start with extended error info.
  404. ;-----------------------------------------------------------------------------
  405. CLEAN_UP:
  406.         PUSH    CS
  407.         POP     DS
  408.         ASSUME  DS:CODE
  409.         CMP     WORD PTR DOS_VERSION,30AH
  410.         JB      SKIP_ERR_RESTORE
  411.         MOV     AX,5D0AH                ;Restore ext error info
  412.         MOV     DX,OFFSET ERRINFOARRAY  ;point to saved info
  413.         INT     21H
  414. SKIP_ERR_RESTORE:
  415. ;-----------------------------------------------------------------------------
  416. ;If using EMS memory, restore EMS mapping context.
  417. ;-----------------------------------------------------------------------------
  418.         CMP     EMS_FLAG,0
  419.         JE      EMS_RESTORE_SKIP
  420.         MOV     AH,48H                  ;Restore mapping context
  421.         MOV     DX,EMS_HANDLE
  422.         INT     67H
  423. EMS_RESTORE_SKIP:
  424. ;-----------------------------------------------------------------------------
  425. ;Restore PSP and DTA
  426. ;-----------------------------------------------------------------------------
  427.         MOV     BX,SAVED_PSP            ;Get old PSP
  428.         MOV     AH,50H                  ;Restore PSP
  429.         CALL    DOSPSPCALL
  430.         PUSH    DS
  431.         LDS     DX,[SAVED_DTA]
  432.         MOV     AH,1AH                  ;Restore DTA
  433.         INT     21H
  434. ;-----------------------------------------------------------------------------
  435. ;Reset the displaced interrupt 1Bh, 23h, and 24h vectors.
  436. ;-----------------------------------------------------------------------------
  437.         MOV     AX,2524H                ;reset int 24h vector
  438.         LDS     DX,CS:[VECTOR24H]
  439.         INT     21H
  440.         MOV     AX,2523H                ;reset int 24h vector
  441.         LDS     DX,CS:[VECTOR23H]
  442.         INT     21H
  443.         MOV     AX,251BH                ;reset int 1Bh vector
  444.         LDS     DX,CS:[VECTOR1BH]
  445.         INT     21H
  446.         POP     DS
  447. ;-----------------------------------------------------------------------------
  448. ;Restore register values, switch back to original stack, and return to caller.
  449. ;-----------------------------------------------------------------------------
  450. MAIN_EXIT:
  451.         CALL    RESTORE_REGS            ;Restore registers
  452.         ASSUME  DS:NOTHING
  453.         CLI                             ;make sure interrupts are off
  454.         MOV     SS,CS:SS_REGISTER       ;switch to original stack
  455.         MOV     SP,CS:SP_REGISTER
  456.         STI                             ;interrupts on
  457.         DEC     CS:[ACTIVE]             ;clear program status flag
  458.         RET                             ;Return to interrupt routine
  459. MAIN            ENDP
  460.  
  461. ;-----------------------------------------------------------------------------
  462. ; GETPATHFILE copies the path from the queue and appends the filename from the
  463. ; DTA assumed to be at offset 80h.
  464. ;   Entry:  ES:SI - ASCIIZ path (can include filename.)
  465. ;           DL - 0 = Source file, always append filename from DTA.
  466. ;                1 = Destination file, append filename only if path specified.
  467. ;   Exit:   CF clear - file opened.
  468. ;           CF set   - error on file open.
  469. ;-----------------------------------------------------------------------------
  470. GETPATHFILE     PROC    NEAR
  471.         ASSUME  CS:CODE
  472.         PUSH    AX
  473.         MOV     CX,75                   ;Max length of string
  474.         MOV     DI,CS:DATA_BUFF_START   ;Copy name to data buffer
  475. GETPATHFILE0:
  476.         LODSB                           ;Get a byte
  477.         STOSB                           ;Store a byte
  478.         OR      AL,AL                   ;are we at the end?
  479.         JNE     GETPATHFILE0            ;No, loop back
  480.         OR      DL,DL                   ;See if source or dest file
  481.         JE      GETPATHFILE1
  482.         CMP     BYTE PTR ES:[DI-2],"\"  ;Is the a path specification?
  483.         JNE     GETPATHFILE_EXIT        ;No, don't append src filename
  484. GETPATHFILE1:
  485.         STD                             ;scan backwards
  486.         MOV     CX,15                   ;Find last \ in filename
  487.         MOV     AL,"\"
  488.         DEC     DI                      ;Back up before 0
  489.         REPNE   SCASB
  490.         CLD
  491.         INC     DI                      ;move DI past \
  492.         INC     DI
  493.         PUSH    DS
  494.         PUSH    CS
  495.         POP     DS
  496.         MOV     SI,OFFSET COMMAND_TAIL+1EH
  497.         MOV     CX,13                   ;copy filename found
  498.         REP     MOVSB
  499.         POP     DS
  500. GETPATHFILE_EXIT:
  501.         POP     AX
  502.         RET
  503. GETPATHFILE     ENDP
  504.  
  505. ;-----------------------------------------------------------------------------
  506. ; GET DEST NAME creates the destination file name from the queue.
  507. ;  Exit:  DX - points to fully specified destination filename.
  508. ;-----------------------------------------------------------------------------
  509. GET_DEST_NAME   PROC    NEAR
  510.         ASSUME  CS:CODE
  511.         MOV     DI,CS:QUEUE_HEAD_PTR    ;Get pointer to filename
  512.         ADD     DI,6                    ;point to file names
  513.         XOR     AL,AL                   ;search for end of 1st name
  514.         MOV     CX,75
  515.         REPNE   SCASB
  516.         MOV     SI,DI                   ;copy pointer
  517.         MOV     DL,1                    ;indicate destination file
  518.         CALL    GETPATHFILE
  519.         MOV     DX,CS:DATA_BUFF_START   ;point to filename
  520.         RET
  521. GET_DEST_NAME   ENDP
  522. ;-----------------------------------------------------------------------------
  523. ; DOSPSPCALL modifies critical error flag on PSP calls to DOS is using 2.x
  524. ;-----------------------------------------------------------------------------
  525. DOSPSPCALL      PROC    NEAR
  526.         ASSUME  CS:CODE
  527.         CMP     WORD PTR CS:[DOS_VERSION],30AH  ;See if DOS < 3.1
  528.         JAE     DOSPSPCALL_OK              ;no, just call DOS
  529.         PUSH    DS
  530.         PUSH    DI
  531.         LDS     DI,CS:CEF_PTR           ;retrieve crit err flag adr
  532.         INC     BYTE PTR [DI]           ;Set DOS in crit error state
  533.         POP     DI
  534.         POP     DS
  535.         INT     21H                     ;Call   DOS
  536.         PUSH    DS
  537.         PUSH    DI
  538.         LDS     DI,CS:CEF_PTR           ;retrieve crit err flag adr
  539.         DEC     BYTE PTR [DI]           ;Set DOS in crit error state
  540.         POP     DI
  541.         POP     DS
  542.         RET
  543. DOSPSPCALL_OK:
  544.         INT     21H                     ;Call DOS
  545.         RET
  546. DOSPSPCALL      ENDP
  547.  
  548. ;-----------------------------------------------------------------------------
  549. ; SAVEREGS saves all the registers used in the interrupt routines and sets DS.
  550. ;-----------------------------------------------------------------------------
  551. SAVE_REGS       PROC    NEAR
  552.         POP     CS:[RET_ADDR]           ;Get address to return to
  553.         PUSH    AX                      ;save all registers
  554.         PUSH    BX
  555.         PUSH    CX
  556.         PUSH    DX
  557.         PUSH    BP
  558.         PUSH    SI
  559.         PUSH    DI
  560.         PUSH    DS
  561.         PUSH    ES
  562.         PUSH    CS                      ;Set DS = CS
  563.         POP     DS
  564.         ASSUME  DS:CODE
  565.         JMP     WORD PTR [RET_ADDR]     ;Return
  566. SAVE_REGS       ENDP
  567.  
  568. ;-----------------------------------------------------------------------------
  569. ;RESTOREREGS restores register values.
  570. ;-----------------------------------------------------------------------------
  571. RESTORE_REGS    PROC    NEAR
  572.         POP     RET_ADDR                ;Save return address
  573.         POP     ES                      ;restore registers
  574.         POP     DS
  575.         ASSUME  DS:NOTHING
  576.         POP     DI
  577.         POP     SI
  578.         POP     BP
  579.         POP     DX
  580.         POP     CX
  581.         POP     BX
  582.         POP     AX
  583.         JMP     WORD PTR CS:[RET_ADDR]  ;Return
  584. RESTORE_REGS    ENDP
  585. ;-----------------------------------------------------------------------------
  586. ; REMOVE deallocates the memory block addressed by ES and restores the
  587. ; interrupt vectors displaced on installation.
  588. ;   Entry:  ES - segment to release
  589. ;   Exit:   CF clear - program uninstalled
  590. ;           CF set   - can't uninstall
  591. ;-----------------------------------------------------------------------------
  592. REMOVE          PROC    NEAR
  593.         ASSUME  CS:CODE
  594.         INC     CS:ACTIVE
  595.         CALL    SAVE_REGS               ;Save registers
  596.         ASSUME  DS:CODE
  597. ;
  598. ;Make sure none of the vectors has been altered.
  599. ;
  600.         MOV     AL,8                    ;check interrupt 8 vector
  601.         CALL    CHECKVECTOR
  602.         JNE     REMOVE_ERROR
  603.         MOV     AL,13H                  ;check interrupt 13h vector
  604.         CALL    CHECKVECTOR
  605.         JNE     REMOVE_ERROR
  606.         MOV     AL,28H                  ;check interrupt 28h vector
  607.         CALL    CHECKVECTOR
  608.         JNE     REMOVE_ERROR
  609. ;
  610. ;If using EMS memory, release it.
  611. ;
  612.         CMP     EMS_FLAG,0
  613.         JE      SKIP_REMOVE_EMS
  614.         MOV     AH,45H                  ;Deallocate pages
  615.         MOV     DX,EMS_HANDLE
  616.         INT     67H
  617.         OR      AH,AH
  618.         JNE     REMOVE_ERROR
  619. SKIP_REMOVE_EMS:
  620. ;
  621. ;Release the memory occupied by the program.
  622. ;
  623.         PUSH    CS
  624.         POP     ES
  625.         MOV     AH,49H                  ;free memory given to
  626.         INT     21H                     ;  original program block
  627.         JC      REMOVE_ERROR            ;branch on error
  628. ;
  629. ;Restore the interrupt 8, 13h, and 28h vectors.
  630. ;
  631.         PUSH    DS                      ;save DS
  632.         ASSUME  DS:NOTHING
  633.         MOV     AX,2508H                ;restore interrupt 8 vector
  634.         LDS     DX,ES:[INT8H]
  635.         INT     21H
  636.         MOV     AX,2513H                ;restore interrupt 13h vector
  637.         LDS     DX,ES:[INT13H]
  638.         INT     21H
  639.         MOV     AX,2528H                ;restore interrupt 28h vector
  640.         LDS     DX,ES:[INT28H]
  641.         INT     21H
  642.         POP     DS                      ;restore DS
  643.         ASSUME  DS:CODE
  644. ;
  645. ;Destroy the ASCII fingerprint that identifies the code and exit.
  646. ;
  647.         NOT     WORD PTR [BEGIN]        ;destroy fingerprint
  648.         CLC                             ;clear CF for exit
  649. REMOVE_EXIT:
  650.         DEC     ACTIVE
  651.         CALL    RESTORE_REGS
  652.         RET                             ;exit with CF intact
  653. ;
  654. ;The program can't be uninstalled.  Set CF and exit.
  655. ;
  656. REMOVE_ERROR:   STC
  657.         JMP     REMOVE_EXIT
  658. REMOVE          ENDP
  659.  
  660. ;-----------------------------------------------------------------------------
  661. ; CHECKVECTOR is called by REMOVE to compare the segment pointed to by an
  662. ; interrupt vector against a segment value supplied by the caller.
  663. ;   Entry:  AL - interrupt number
  664. ;   Exit:   ZF clear - segments do not match
  665. ;           ZF set   - segments match
  666. ;-----------------------------------------------------------------------------
  667. CHECKVECTOR     PROC    NEAR
  668.         PUSH    CS
  669.         POP     CX
  670.         MOV     AH,35H                  ;get vector
  671.         INT     21H
  672.         MOV     AX,ES                   ;transfer segment to AX
  673.         CMP     AX,CX                   ;compare
  674.         RET
  675. CHECKVECTOR     ENDP
  676.  
  677. ;-----------------------------------------------------------------------------
  678. ; FINALINSTALL is called to setup the data queue and to TSR.
  679. ;-----------------------------------------------------------------------------
  680. FINALINSTALL    PROC    NEAR
  681.     ASSUME  CS:CODE,DS:CODE,ES:CODE
  682.         MOV     DI,[QUEUE_HEAD_PTR]
  683.         MOV     ES,DATA_SEGMENT
  684.         MOV     SI,OFFSET END_OF_CODE   ;point to filenames
  685.         CALL    PUTINQUEUE              ;Load files in queue
  686.         MOV     DATA_BUFF_START,DI      ;Set start of data buffer
  687.         INC     FILE_COUNT              ;Inc file count
  688. ;
  689. ;Terminate and remain resident in memory.
  690. ;
  691.         DEC     ACTIVE                  ;allow background task
  692.         MOV     AX,3100H                ;terminate with ERRORLEVEL = 0
  693.         MOV     DX,(OFFSET END_OF_DATA-OFFSET CODE+15) SHR 4
  694.         CMP     EMS_FLAG,0
  695.         JE      FINAL_INSTALL1
  696.         MOV     DX,(OFFSET DATA_BUFFER-OFFSET CODE+15) SHR 4
  697. FINAL_INSTALL1:
  698.         INT     21H
  699. FINALINSTALL    ENDP
  700.  
  701. ;-----------------------------------------------------------------------------
  702. ; PUTINQUEUE is called copy the fully qualified filenames into the queue.
  703. ;  Entry:  ES:DI - Points to open entry in queue.
  704. ;          DS:SI - Points to entry to put in queue.
  705. ;-----------------------------------------------------------------------------
  706. PUTINQUEUE      PROC    NEAR
  707.         ASSUME  CS:CODE,DS:CODE
  708.         MOV     BX,DI                   ;save pointer
  709.         MOV     DX,2                    ;move two asciiz strings
  710.         ADD     DI,DX                   ;move past pointer
  711.         XOR     AX,AX
  712.         STOSW                           ;initialize file flags
  713.         STOSW
  714. COPY_LOOP1:
  715.         LODSB                           ;Copy filename
  716.         STOSB
  717.         CMP     AL,0
  718.         JNE     COPY_LOOP1
  719.         DEC     DL                      ;decriment name counter
  720.         JNE     COPY_LOOP1
  721.         MOV     ES:[BX],DI              ;point to next available
  722.         MOV     WORD PTR ES:[DI],0FFFFH ;  space in queue
  723.         INC     DI
  724.         INC     DI
  725.         RET
  726. PUTINQUEUE      ENDP
  727.  
  728. ;=============================================================================
  729. ; Buffer space to be used once the program is resident.
  730. ;=============================================================================
  731. PC              = $
  732. PC              = PC + 256
  733. STACK_SPACE     = PC                            ;stack for resident routine
  734. DATA_BUFFER     = PC                            ;Buffer for data
  735. PC              = PC + 4096
  736. END_OF_DATA     = PC
  737.  
  738. ;=============================================================================
  739. ; INITIALIZE
  740. ;=============================================================================
  741. ERRMSG1         DB      "Source file not found$"
  742. ERRMSG2         DB      "Bad target path$"
  743. ERRMSG3         DB      "Can't install$"
  744. ERRMSG4         DB      "Dup. file names$"
  745. ERRMSG5         DB      "Bad Switch$"
  746. ERRMSG6         DB      "(Already installed)$"
  747. ERRMSG7         DB      "EMS driver error$"
  748. ERRMSG8        DB    "Deinstall queued$"
  749.  
  750. OTHER_SEG       DW      0                       ;Segment of installed code
  751. ALRDY_INSTALLED DB      0                       ;bcopy already installed flag
  752. DEF_DISK        DB      ?                       ;Default disk drive
  753. EMS_HEADER      DB      "EMMXXXX0"              ;EMS driver header
  754.  
  755. INITIALIZE      PROC    NEAR
  756.         ASSUME CS:CODE, DS:CODE
  757.  
  758.         MOV     DX,OFFSET PROGRAM
  759.         CALL    MESSAGE
  760.         CLD                             ;clear DF
  761. ;-----------------------------------------------------------------------------
  762. ;Get DOS version.
  763. ;-----------------------------------------------------------------------------
  764.         MOV     AH,30H                  ;Get DOS version
  765.         INT     21H
  766.         XCHG    AL,AH                   ;Put version in proper order
  767.         MOV     DOS_VERSION,AX          ;Save DOS version
  768. ;-----------------------------------------------------------------------------
  769. ;Get default disk drive.
  770. ;-----------------------------------------------------------------------------
  771.         MOV     AH,19H                  ;Get default disk
  772.         INT     21H
  773.         INC     AL
  774.         MOV     DEF_DISK,AL
  775. ;-----------------------------------------------------------------------------
  776. ;See if a copy is already resident in memory. If no copy, install.
  777. ;-----------------------------------------------------------------------------
  778.         NOT     WORD PTR [BEGIN]        ;initialize fingerprint
  779.         MOV     BX,600H                 ;zero BX for start
  780.         MOV     AX,CS                   ;keep CS value in AX
  781. FIND_COPY:
  782.         INC     BX                      ;increment search segment value
  783.         MOV     ES,BX
  784.         CMP     AX,BX                   ;not installed if current
  785.         JE      FIND_COPY1              ;  segment is looped back to
  786.         MOV     SI,OFFSET BEGIN         ;search this segment for ASCII
  787.         MOV     DI,SI                   ;  fingerprint
  788.         MOV     CX,16
  789.         REPE    CMPSB
  790.         JNE     FIND_COPY               ;loop back if not found
  791.         INC     ALRDY_INSTALLED         ;Clear installed flag
  792.         MOV    DX,OFFSET ERRMSG6
  793.         CALL    MESSAGE
  794. FIND_COPY1:
  795.         INC     ES:[ACTIVE]             ;Don't let background active
  796.         MOV     OTHER_SEG,ES            ;save installed code segment
  797.         PUSH    CS
  798.         POP     ES
  799. ;-----------------------------------------------------------------------------
  800. ;Parse the command line for remove switch.
  801. ;-----------------------------------------------------------------------------
  802.         MOV     DI,OFFSET COMMAND_TAIL  ;Point SI to command line text
  803.         MOV     CL,[DI]                 ;Get length of command line.
  804.  
  805.         OR    CL,CL
  806.         JNZ    SWITCHES
  807.         JMP    TERMINATE
  808. SWITCHES:
  809.         XOR     CH,CH
  810.         INC     DI
  811.         PUSH    CX                      ;Save pointer for later use.
  812.         PUSH    DI
  813. SWITCHES1:
  814.         MOV     AL,"/"                  ;Put switch in AL
  815.         REPNE   SCASB                   ;Scan for cmd line switches
  816.         JNE     PARSE
  817.         MOV     AH,[DI]
  818.         AND     AH,0DFH                 ;Convert to caps
  819.         CMP     AH,"U"                  ;See if uninstall switch
  820.         JE      SWITCHES2
  821.         CMP     AH,"X"                  ;See if expanded memory switch
  822.         JE      SWITCHES3
  823.         MOV     DX,OFFSET ERRMSG5       ;Illegal switch
  824.         ADD     SP,4                    ;Clean up stack
  825.         JMP     DISP_ERROR
  826. SWITCHES2:
  827.         PUSH    ES
  828.         MOV     ES,OTHER_SEG
  829.         INC     ES:[REMOVE_FLAG]        ;Set uninstall flag
  830.         POP     ES
  831.         MOV    DX,OFFSET ERRMSG8
  832.         CALL    MESSAGE
  833.         JMP     SWITCHES1
  834. SWITCHES3:
  835.         CMP     ALRDY_INSTALLED,0
  836.         JE      SWITCHES4
  837.         MOV     DX,OFFSET ERRMSG5
  838.         CALL    MESSAGE
  839.         JMP     SWITCHES1
  840. SWITCHES4:
  841.         INC     EMS_FLAG                ;Set expanded memory flag
  842.         JMP     SWITCHES1
  843. ;-----------------------------------------------------------------------------
  844. ;Parse the command line and create a complete filespec.
  845. ;-----------------------------------------------------------------------------
  846. PARSE:        POP     SI                      ;Get back pointer to cmd line
  847.         POP     CX
  848.         MOV     DI,OFFSET END_OF_CODE   ;save fully specified name
  849.         CALL    PARSE_FILENAME          ;  in a safe place.
  850.         PUSH    DI                      ;save 2nd file pointer
  851.         DEC     SI
  852.         CALL    PARSE_FILENAME          ;process 2nd filename
  853.         POP     SI
  854. ;-----------------------------------------------------------------------------
  855. ;Verify that source file exists.
  856. ;-----------------------------------------------------------------------------
  857.         MOV     DX,OFFSET END_OF_CODE   ;point to source filename
  858.         MOV     CX,00                   ;normal file search
  859.         MOV     AH,4EH                  ;Find first
  860.         INT     21H
  861.         MOV     DX,SI                   ;get 2nd file pointer
  862.         JNC     VERIFY_DEST
  863.         MOV     DX,OFFSET ERRMSG1       ;File not found
  864.         JMP     DISP_ERROR              ;Print error msg and terminate
  865. ;-----------------------------------------------------------------------------
  866. ;Determine if the destination specification is a path or a complete filename.
  867. ;See if path by setting default path to destination.
  868. ;-----------------------------------------------------------------------------
  869. VERIFY_DEST:    PUSH    DX
  870.         MOV     DX,[SI]                 ;Get destination disk
  871.         MOV     SI,OFFSET END_OF_CODE + 300
  872.         MOV     [SI],DX                 ;Save current path in
  873.         MOV     BYTE PTR [SI+2],"\"     ;  safe place.
  874.         ADD     SI,3
  875.         SUB     DL,40H                  ;Convert to hex
  876.         MOV     AH,47H                  ;Get current directory
  877.         INT     21H
  878.         POP     DX
  879.         JC      VERIFY_ERROR
  880.         MOV     AH,3BH                  ;Set path to destination
  881.         INT     21H
  882.         JC      VERIFY1
  883.         MOV     DX,SI                   ;Point DX to saved path
  884.         SUB     DX,3
  885.         MOV     AH,3BH                  ;Restore path
  886.         INT     21H
  887.         CMP     BYTE PTR [DI-2],"\"
  888.         JE      VERIFY_DONE
  889.         MOV     WORD PTR [DI-1],005CH   ;Append \ to indicate path
  890.         JMP     SHORT VERIFY_DONE
  891. ;-----------------------------------------------------------------------------
  892. ;Try failed, search for file.
  893. ;-----------------------------------------------------------------------------
  894. VERIFY1:    MOV     CX,0                    ;Normal search
  895.         MOV     AH,4EH                  ;Find first
  896.         INT     21H
  897.         JNC     VERIFY_DONE             ;file found, were done.
  898.         CMP     AX,3                    ;check for bad path
  899.         JNE     VERIFY_DONE             ;path found, must be file
  900. VERIFY_ERROR:
  901.         MOV     DX,OFFSET ERRMSG2       ;bad path, print path
  902.         JMP     DISP_ERROR        ;  not found.
  903. VERIFY_DONE:
  904. ;-----------------------------------------------------------------------------
  905. ;Check to see if the filenames are the same.
  906. ;-----------------------------------------------------------------------------
  907.         MOV     SI,OFFSET END_OF_CODE
  908.         MOV     DI,DX
  909.         MOV     CX,DX                   ;Compute length of name
  910.         SUB     CX,SI
  911.         REPE    CMPSB                   ;compare strings
  912.         JE      SAME_NAMES
  913.         MOV     AX,005CH
  914.         CMP     [DI-2],AX               ;See if dest is path
  915.         JNE     COMPARE_NAMES1
  916.         MOV     DI,SI                   ;Scan source for \. If not
  917.         REPNE   SCASB                   ;  found, src and dest are
  918.         JE      COMPARE_NAMES1          ;  in the same directory.
  919. SAME_NAMES:
  920.         MOV     DX,OFFSET ERRMSG4       ;Same, print error message
  921.         JMP     SHORT DISP_ERROR
  922. COMPARE_NAMES1:
  923.         CMP     ALRDY_INSTALLED,0       ;see if bcopy installed
  924.         JE      INSTALL
  925. ;-----------------------------------------------------------------------------
  926. ;Add new file names to installed code's queue and terminate.
  927. ;-----------------------------------------------------------------------------
  928. LOAD_FILES:
  929.         MOV     SI,OFFSET END_OF_CODE   ;point to file names
  930.         PUSH    DS
  931.         MOV     DS,OTHER_SEG            ;point ES to installed code
  932.         ASSUME  DS:NOTHING
  933. LOAD_QUEUE0:
  934.         CMP     DS:[ACTIVE],1           ;Wait till background not
  935.         JNE     LOAD_QUEUE0             ;  active.
  936.         CMP    DS:EMS_FLAG,1        ;See if we need to map
  937.         JNE    LOAD_QUEUE1             ;  Expanded memory.
  938.         MOV    DX,DS:EMS_HANDLE
  939.         MOV    AX,4400H        ;Map to physical page 0
  940.         XOR    BX,BX            ;Map logical page 0
  941.         INT    67H
  942.         OR    AH,AH            ;Check for error
  943.         JE     LOAD_QUEUE1
  944.         JMP    EMS_ERROR
  945. LOAD_QUEUE1:
  946.         MOV     DI,DS:[QUEUE_HEAD_PTR]  ;Get start of queue.
  947.         MOV     ES,DS:[DATA_SEGMENT]
  948. LOAD_QUEUE2:
  949.         MOV     AX,0FFFFH
  950.         CMP     ES:[DI],AX              ;Is queue empty?
  951.         JE      LOAD_QUEUE3             ;yes, continue
  952.         MOV     DI,ES:[DI]              ;Get pointer to next file
  953.         JMP     LOAD_QUEUE2
  954. LOAD_QUEUE3:
  955.         POP     DS                      ;restore DS
  956.         ASSUME  DS:CODE
  957.         CALL    PUTINQUEUE              ;copy filenames into queue
  958.         MOV     ES,OTHER_SEG
  959.         MOV     ES:[DATA_BUFF_START],DI
  960.         INC     ES:FILE_COUNT
  961. ;-----------------------------------------------------------------------------
  962. ;Enable background task and terminate.
  963. ;-----------------------------------------------------------------------------
  964. TERMINATE:
  965.         DEC     ES:[ACTIVE]             ;enable background task.
  966.         MOV     AX,4C00H                ;Terminate with RC = 0.
  967.         INT     21H
  968.  
  969. ;=============================================================================
  970. ;Display error message and exit with Return Code = 1.
  971. ;=============================================================================
  972. DISP_ERROR:    CALL    MESSAGE
  973.         MOV     ES,OTHER_SEG            ;point ES to installed code
  974.         DEC     ES:[ACTIVE]             ;enable background task.
  975.         MOV     AX,4C01H                ;Exit RC = 1
  976.         INT     21H
  977.  
  978. ;=============================================================================
  979. ;Install. Get address of INDOS flag.
  980. ;=============================================================================
  981. INSTALL:
  982.         MOV     AH,34H                  ;get address from DOS
  983.         INT     21H
  984.         MOV     WORD PTR INDOS,BX       ;store it
  985.         MOV     WORD PTR INDOS[2],ES
  986. ;-----------------------------------------------------------------------------
  987. ;Find and save the address of the DOS critical error flag.
  988. ;-----------------------------------------------------------------------------
  989.         MOV     AX,3E80H                ;CMP opcode
  990.         MOV     CX,2000H                ;max search length
  991.         MOV     DI,BX                   ;start at INDOS address
  992. CEFS1:          REPNE   SCASW                   ;do the search
  993.         JCXZ    CEFS2                   ;branch if search failed
  994.         CMP     BYTE PTR ES:[DI+5],0BCH ;verify this is it
  995.         JE      CEFSFOUND               ;branch if it is
  996.         JMP     CEFS1                   ;resume loop if it's not
  997. CEFS2:          MOV     CX,2000H                ;search again
  998.         INC     BX                      ;search odd addresses this time
  999.         MOV     DI,BX
  1000. CEFS3:          REPNE   SCASW                   ;look for the opcode
  1001.         JCXZ    CEFSNOTFOUND            ;not found if loop expires
  1002.         CMP     BYTE PTR ES:[DI+5],0BCH ;verify this is it
  1003.         JE      CEFSFOUND
  1004.         JMP     CEFS3
  1005. CEFSNOTFOUND:
  1006.         MOV     DX,OFFSET ERRMSG3       ;Critical error flag not found
  1007.         JMP     SHORT DISP_ERROR
  1008. CEFSFOUND:      MOV     AX,ES:[DI]              ;get flag offset address
  1009.         MOV     WORD PTR CEF_PTR,AX     ;store it
  1010.         MOV     WORD PTR CEF_PTR[2],ES
  1011. ;-----------------------------------------------------------------------------
  1012. ;Set up EMS memory if necessary.
  1013. ;-----------------------------------------------------------------------------
  1014.         PUSH    CS
  1015.         POP     DATA_SEGMENT            ;assume no ems memory
  1016.         CMP     EMS_FLAG,0
  1017.         JE      SKIP_EMS_SETUP
  1018. ;Test for the EMS driver.
  1019.         PUSH    ES                      ;Test for ems driver
  1020.         PUSH    DI
  1021.         MOV     AX,3567H                ;Get EMS vector
  1022.         INT     21H
  1023.         MOV     DI,0AH                  ;Using the segment from the
  1024.         MOV     SI,OFFSET EMS_HEADER    ;  67h vector, look at offset
  1025.         MOV     CX,8                    ;  0ah. Compare the next 8
  1026.         CLD                             ;  bytes with the expected
  1027.         REPE    CMPSB                   ;  EMS header. If  they are
  1028.         POP     DI                      ;  the same, EMS driver
  1029.         POP     ES                      ;  found.
  1030.         JE      EMS_FOUND
  1031. EMS_ERROR:
  1032.         MOV     DX,OFFSET ERRMSG7       ;EMS driver error
  1033.         JMP     DISP_ERROR
  1034. EMS_FOUND:
  1035.         STC
  1036.         MOV     AH,40H                  ;Check status
  1037.         INT     67H
  1038.         OR      AH,AH
  1039.         JNE     EMS_ERROR
  1040.         MOV     AH,41H                  ;Get page frame segment
  1041.         INT     67H
  1042.         OR      AH,AH
  1043.         JNE     EMS_ERROR
  1044.         MOV     DATA_SEGMENT,BX         ;Save seg of EMS page frame
  1045.         MOV     AH,43H                  ;Get page frame segment
  1046.         MOV     BX,1                    ;request 1 page
  1047.         INT     67H
  1048.         OR      AH,AH
  1049.         JNE     EMS_ERROR
  1050.         MOV     EMS_HANDLE,DX           ;Save EMS handle
  1051.         MOV     AX,4400H                ;Map page
  1052.         XOR     BX,BX                   ;map page 1
  1053.         MOV     DX,EMS_HANDLE           ;identify user
  1054.         INT     67H
  1055.         OR      AH,AH
  1056.         JNE     EMS_ERROR
  1057.         MOV     DATA_BUFF_END,4096      ;set buffer parameters
  1058.         MOV     DATA_BUFF_START,0
  1059. SKIP_EMS_SETUP:
  1060. ;-----------------------------------------------------------------------------
  1061. ;Initialize pointers needed for queue.
  1062. ;-----------------------------------------------------------------------------
  1063.         MOV     BX,DATA_BUFF_START      ;initialize queue
  1064.         MOV     QUEUE_HEAD_PTR,BX
  1065. ;-----------------------------------------------------------------------------
  1066. ;Set interrupt 8h, 13h, and 28h vectors to internal handlers.
  1067. ;-----------------------------------------------------------------------------
  1068.         MOV     AX,3508H                ;interrupt 8
  1069.         INT     21H
  1070.         MOV     WORD PTR INT8H,BX
  1071.         MOV     WORD PTR INT8H[2],ES
  1072.         MOV     AX,2508H
  1073.         MOV     DX,OFFSET TIMERINT
  1074.         INT     21H
  1075.  
  1076.         MOV     AX,3513H                ;interrupt 13h
  1077.         INT     21H
  1078.         MOV     WORD PTR INT13H,BX
  1079.         MOV     WORD PTR INT13H[2],ES
  1080.         MOV     AX,2513H
  1081.         MOV     DX,OFFSET DISKINT
  1082.         INT     21H
  1083.  
  1084.         MOV     AX,3528H                ;interrupt 28h
  1085.         INT     21H
  1086.         MOV     WORD PTR INT28H,BX
  1087.         MOV     WORD PTR INT28H[2],ES
  1088.         MOV     AX,2528H
  1089.         MOV     DX,OFFSET IDLE
  1090.         INT     21H
  1091. ;-----------------------------------------------------------------------------
  1092. ;Deallocate the program's environment block.
  1093. ;-----------------------------------------------------------------------------
  1094.         MOV     AX,DS:[2CH]             ;get environment segment
  1095.         MOV     ES,AX
  1096.         MOV     AH,49H                  ;free it
  1097.            INT     21H
  1098.         PUSH    CS                      ;reset ES to the code segment
  1099.         POP     ES
  1100.         JMP     FINALINSTALL            ;Jump to code above data area
  1101. INITIALIZE      ENDP
  1102.  
  1103. ;======================================================================
  1104. ; Send message to screen
  1105. ;----------------------------------------------------------------------
  1106. CRLF$        DB    13,10,"$"
  1107.  
  1108. MESSAGE        PROC    NEAR
  1109.         ASSUME  CS:CODE,DS:CODE
  1110.  
  1111.         MOV    AH,9
  1112.         INT    21H
  1113.         MOV    DX,OFFSET CRLF$
  1114.         MOV    AH,9
  1115.         INT    21H
  1116.         RET
  1117.  
  1118. MESSAGE        ENDP
  1119.  
  1120. ;======================================================================
  1121. ; PARSE_FILENAME  creates a proper pathname for a filename
  1122. ;   Entry:  SI - pointer to asciiz filename
  1123. ;           DI - pointer to buffer to hold resulting pathname
  1124. ;-----------------------------------------------------------------------------
  1125. PARSE_FILENAME  PROC    NEAR
  1126.         ASSUME  CS:CODE,DS:CODE,ES:CODE
  1127. PARSE0:
  1128.         CALL    SCANFORSEP              ;Scan till character found
  1129.         LAHF                            ;Save return flags
  1130.         CMP     AL,0DH                  ;See if carrage return or
  1131.         JE      PARSE01                 ;  command switch.
  1132.         CMP     AL,"/"
  1133.         JE      PARSE01
  1134.         SAHF
  1135.         JC      PARSE0
  1136.         JMP     SHORT PARSE02
  1137. PARSE01:
  1138.         MOV     WORD PTR [SI-1],0       ;If so, fake a null file name
  1139. PARSE02:
  1140. ;Look for disk specification
  1141.         DEC     SI                      ;backup to before 1st char
  1142.         CMP     BYTE PTR 1[SI],":"      ;see if disk specified
  1143.         JE      PARSE1
  1144.         MOV     AL,DEF_DISK             ;Get default disk
  1145.         MOV     DL,AL                   ;save default disk number
  1146.         ADD     AL,40H                  ;Convert to ascii
  1147.         MOV     AH,":"
  1148.         JMP     SHORT PARSE2
  1149. PARSE1:
  1150.         LODSW                           ;Get disk specified
  1151.         AND     AL,0DFH                 ;convert to caps
  1152.         MOV     DL,AL
  1153.         SUB     DL,40H                  ;convert to binary
  1154. PARSE2:
  1155.         STOSW                           ;Load disk specification
  1156. ;Look for directory specification.
  1157.         MOV     BX,DI                   ;save start of path
  1158.         MOV     AL,"\"
  1159.         CMP     BYTE PTR [SI],AL        ;See if starting from root
  1160.         JE      PARSE3                  ;yes, skip append of path
  1161.         STOSB                           ;Start at root
  1162.         PUSH    SI                      ;save current pointer
  1163.         MOV     SI,DI                   ;point to dest buffer
  1164.         MOV     AH,47H                  ;Get default path
  1165.         INT     21H
  1166.         MOV     CX,64                   ;Max 64 char in path
  1167.         MOV     AL,0
  1168.         REPNE   SCASB                   ;scan for end of path
  1169.         DEC     DI                      ;move back before zero
  1170.         POP     SI
  1171.         CMP     CX,63                   ;If no path, don't append an
  1172.         JGE     PARSE3                  ;  extra \.
  1173. PARSE21:
  1174.         CALL    SCANFORSEP              ;Get first char of name.
  1175.         JC      PARSE5                  ;If no name, skip append
  1176.         DEC     SI                      ;Backup so char can be reread
  1177.         MOV     AL,"\"                  ;If name, seperate from path
  1178.         STOSB                           ; with a \.
  1179. PARSE3:
  1180. ;Look for filename specification.
  1181.         MOV     CX,75                   ;max 75 characters in a name
  1182.         XOR     AH,AH                   ;Clear last char holder
  1183. PARSE4:
  1184.         CALL    SCANFORSEP              ;Get character, if seperator
  1185.         JC      PARSE5                  ;  char, exit.
  1186.         STOSB                           ;Good char, copy to dest.
  1187.         CMP     AX,".."                 ;See if last 2 chars are same
  1188.         JNE     PARSE41
  1189.         STD                             ;scan backwards
  1190.         SUB     DI,4                    ;First, backup past '\..'
  1191.         MOV     AL,"\"                  ;Look for directory sep
  1192.         PUSH    CX
  1193.         MOV     CX,DI                   ;compute length of path
  1194.         SUB     CX,BX
  1195.         REPNE   SCASB                   ;Now, past last directory
  1196.         POP     CX
  1197.         CLD                             ;scan forwards again
  1198.         INC     DI                      ;Move back past \
  1199. PARSE41:
  1200.         MOV     AH,AL                   ;save last character read.
  1201.         LOOP    PARSE4
  1202. PARSE5:
  1203.         XOR     AL,AL                   ;Terminate string with 0
  1204.         STOSB
  1205.         RET
  1206. PARSE_FILENAME  ENDP
  1207.  
  1208. ;-----------------------------------------------------------------------------
  1209. ; SCANFORSEP looks for file seperator characters.
  1210. ;   Entry:  DS:SI - pointer to character to compare
  1211. ;   Exit:   CF clear - good character
  1212. ;           CF set   - file seperator character detected
  1213. ;           AL - character read.
  1214. ;-----------------------------------------------------------------------------
  1215. SCANFORSEP      PROC    NEAR
  1216.         LODSB                           ;Get character
  1217.         CMP     AL," "                  ;Look for file delimiters
  1218.         JLE     SCANFOR1                ;  space, comma, semicolon,
  1219.         CMP     AL,","                  ;  equal sign, or tab. Or any
  1220.         JE      SCANFOR1                ;  characters with ASCII
  1221.         CMP     AL,";"                  ;  values less than 20 or
  1222.         JE      SCANFOR1                ;  greater than 127.
  1223.         CMP     AL,"="
  1224.         JE      SCANFOR1
  1225.         CMP     AL,"a"                  ;Convert all letters to caps
  1226.         JB      SCANFOR_END
  1227.         CMP     AL,"z"
  1228.         JA      SCANFOR_END
  1229.         AND     AL,0DFH                 ;convert to caps
  1230. SCANFOR_END:
  1231.         CLC                             ;clear flag
  1232.         RET
  1233. SCANFOR1:
  1234.         STC                             ;set found flag
  1235.         RET
  1236. SCANFORSEP      ENDP
  1237.  
  1238. END_OF_CODE     =       $
  1239. CODE            ENDS
  1240.         END     BEGIN
  1241.